/*
 * CloudStego school project
 * @author lauris kruusamae
 * @author triin uudam
 * @author uku loskit
 */
package main;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * Collection of helper methods 
 *
 */
public class Util {
    
	/**
	 * Encodes the given byte into the pixels in pixels array
	 * @param pixels
	 * @param data
	 * @param isEnd
	 */
    public static void encodeDataIntoPixels(Pixel[] pixels, byte data, boolean isEnd) {
        for (int index = 2; index > -1; index--) {
            Pixel pixel = pixels[index];
            if (index == 2) {
                pixel.setGreen(pixel.getGreen() | (data & 1));
                data >>= 1;
                pixel.setRed(pixel.getRed() | (data & 1));
                data >>= 1;
                if (isEnd) {
                    pixel.setBlue(pixel.getBlue() | 1);
                }
            } else {
                pixel.setBlue(pixel.getBlue() | (data & 1));
                data >>= 1;
                pixel.setGreen(pixel.getGreen() | (data & 1));
                data >>= 1;
                pixel.setRed(pixel.getRed() | (data & 1));
                data >>= 1;
            }
        }
    }
    
    /**
     * Creates an ArrayList of Pixel objects from the given BufferedImage
     * @param image
     * @param clearLSB if true, clears the least significant bit of every pixel
     * @return
     */
    public static ArrayList<Pixel> getPixelsFromImage(final BufferedImage image, final boolean clearLSB) {
        List<int[][]> baseImageRgb = getRgbFromImage(image);
        int[][] r = baseImageRgb.get(0);
        int[][] g = baseImageRgb.get(1);
        int[][] b = baseImageRgb.get(2);

        int baseImageHeight = image.getHeight();
        int baseImageWidth = image.getWidth();
        
        ArrayList<Pixel> pixels = new ArrayList<Pixel>();
        int pixelCount = 0;
        for (int heightIndex = 0; heightIndex < baseImageHeight; heightIndex++) {
            for (int widthIndex = 0; widthIndex < baseImageWidth; widthIndex++) {
            	if (clearLSB) {
            		pixels.add(new Pixel(pixelCount++, r[heightIndex][widthIndex] & ~1, 
            				g[heightIndex][widthIndex] & ~1, b[heightIndex][widthIndex] & ~1));
            	} else {
            		pixels.add(new Pixel(pixelCount++, r[heightIndex][widthIndex], 
            				g[heightIndex][widthIndex], b[heightIndex][widthIndex]));
            	}
            }
        }
        return pixels;
    }
    
    /**
     * Created a BufferedImage from an ArrayList of Pixel objects
     * @param pixels
     * @param imageHeight
     * @param imageWidth
     * @return
     */
    public static BufferedImage getImageFromPixels(final ArrayList<Pixel> pixels, int imageHeight, int imageWidth) {
        int[][] r = new int[imageHeight][imageWidth];
        int[][] g = new int[imageHeight][imageWidth];
        int[][] b = new int[imageHeight][imageWidth];
        
        Pixel pixel;
        int pixelIndex = 0;
        for (int heightIndex = 0; heightIndex < imageHeight; heightIndex++) {
            for (int widthIndex = 0; widthIndex < imageWidth; widthIndex++) {
                if (pixels.isEmpty()) {
                    break;
                }
                pixel = pixels.get(pixelIndex++);
                r[heightIndex][widthIndex] = pixel.getRed();
                g[heightIndex][widthIndex] = pixel.getGreen();
                b[heightIndex][widthIndex] = pixel.getBlue();
            }
        }
        List<int[][]> baseImageRgb = new ArrayList<int[][]>();
        baseImageRgb.add(r);
        baseImageRgb.add(g);
        baseImageRgb.add(b);
        return getImageFromRgb(baseImageRgb);
    }
    
    /**
     * Get RGB data array from given image
     * 
     * @param image
     *            Image
     * @return List with three elements of two-dimensional int's - R, G and B
     */
    public static LinkedList<int[][]> getRgbFromImage(BufferedImage image) {
        LinkedList<int[][]> rgb = new LinkedList<int[][]>();
        int[][] r = null;
        int[][] g = null;
        int[][] b = null;
        int width = 0;
        int height = 0;

        width = image.getWidth();
        height = image.getHeight();

        r = new int[height][width];
        g = new int[height][width];
        b = new int[height][width];

        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                r[i][j] = (image.getRGB(j, i) >> 16) & 0xFF;
                g[i][j] = (image.getRGB(j, i) >> 8) & 0xFF;
                b[i][j] = (image.getRGB(j, i) >> 0) & 0xFF;
            }
        }

        rgb.add(r);
        rgb.add(g);
        rgb.add(b);

        return rgb;
    }
    
    /**
     * Get image from given RGB data
     * 
     * @param rgb
     *            List with three elements of two-dimensional int's - R, G and B
     * @return Image
     */
    public static BufferedImage getImageFromRgb(List<int[][]> rgb) {
        BufferedImage image = null;
        int width = 0;
        int height = 0;
        int[][] r = null;
        int[][] g = null;
        int[][] b = null;

        r = rgb.get(0);
        g = rgb.get(1);
        b = rgb.get(2);

        height = r.length;
        width = r[0].length;
        image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                image.setRGB(j, i, (r[i][j] << 16) + (g[i][j] << 8) + b[i][j]);
            }
        }

        return image;
    }
    
    /**
     * Extracts the message data from the List of Pixel objects
     * Writes the data into the given OutputStream
     * @param out
     * @param pixels
     * @throws IOException
     */
    public static void extractMessage(OutputStream out, List<Pixel> pixels) throws IOException {
        System.out.println(pixels.size());
        int index = 0;
        while (true) {
            Pixel p1 = null;
            Pixel p2 = null;
            Pixel p3 = null;
            if (index != pixels.size() - 1) {
                p1 = pixels.get(index++);
            }
            if (index != pixels.size() - 1) {
                p2 = pixels.get(index++);
            }
            if (index != pixels.size() - 1) {
                p3 = pixels.get(index++);
            }
            
            int data = 0;
            if (p1 != null) {
                data = appendPixelData(data, p1);
            }
            if (p2 != null) {
                data = appendPixelData(data, p2);
            }
            if (p3 != null) {
                data |= p3.getRed() & 1;
                data <<= 1;
                data |= p3.getGreen() & 1;
            }
            out.write(data);
            
            // If the last bit is 1, then finish reading the message
            if (p1 == null || p2 == null || p3 == null) {
                break;
            } else if (p3 != null && (p3.getBlue() & 1) == 1) {
                break;
            }
        }
    }
    
    /**
     * Adds 3 bits from the data to the values of the given pixel
     * @param data
     * @param pixel
     * @return
     */
    private static int appendPixelData(int data, Pixel pixel) {
        data |= pixel.getRed() & 1;
        data <<= 1;
        data |= pixel.getGreen() & 1;
        data <<= 1;
        data |= pixel.getBlue() & 1;
        data <<= 1;
        return data;
    }
    
    public static String getFileExtension(String filePath) {
    	int dotIndex = filePath.lastIndexOf(".");
    	if (dotIndex < filePath.length() - 1) {
        	return filePath.substring(dotIndex + 1); 
    	} else {
    		return "";
    	}
    }
    
    public static String getFileName(String filePath) {
    	String[] split = filePath.split("/");
    	String file = split[split.length - 1];
    	int dotIndex = file.lastIndexOf(".");
    	if (dotIndex == -1) {
    		return file;
    	}
    	return file.substring(0, dotIndex); 
    }
}
